package com.vst.base.orm.impl;

import static com.google.common.base.Preconditions.checkNotNull;

import java.io.Serializable;
import java.util.List;
import javax.inject.Inject;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.stereotype.Component;

import com.google.common.collect.Lists;
import com.vst.orm.base.Hibernatify;
import com.vst.orm.base.Page;
import com.vst.orm.base.Query;
import com.vst.orm.base.exception.NotFoundException;
import com.vst.orm.base.exception.PersistenceException;
import com.vst.orm.dao.hibernate.HibernateBaseDAO;
import com.vst.orm.search.SearchResult;


@Component
public class BaseDao extends HibernateBaseDAO implements Hibernatify {

	public BaseDao() {
		
	}
	
	@Inject
	public BaseDao(final SessionFactory sessionFactory) {
		super.setSessionFactory(sessionFactory);
	}

	public <T, ID extends Serializable> T get(Class<T> type, ID id) {
		T result = this.find(type, id);
		if (result == null) {
			throw new NotFoundException(type, id, null);
		}
		return result;
	}

	public <T, ID extends Serializable> List<T> get(Class<T> type, ID id,
			ID... rest) {
		return this.get(type, Lists.asList(checkNotNull(id), rest));
	}

	public <T, ID extends Serializable> List<T> get(Class<T> type,
			Iterable<ID> ids) {
		final List<T> result = Lists.newArrayList();
		for (ID it : ids) {
			try {
				result.add(this.get(type, it));
			} catch (Exception e) {
				throw new NotFoundException(type, it, e);
			}
		}
		return result;
	}

	public <T, ID extends Serializable> T find(Class<T> type, ID id) {
		return super._get(checkNotNull(type), checkNotNull(id));
	}

	public <T, ID extends Serializable> List<T> find(Class<T> type, ID id,
			ID... rest) {
		return this.find(type, Lists.asList(checkNotNull(id), rest));
	}

	public <T, ID extends Serializable> List<T> find(Class<T> type,
			Iterable<ID> ids) {
		checkNotNull(ids);
		final List<T> result = Lists.newArrayList();
		for (ID it : ids) {
			result.add(this.find(type, it));
		}
		return result;
	}

	public <T> List<T> find(Class<T> type) {
		checkNotNull(type);
		return this.list(new QueryImpl<T, T>(type));
	}

	public <T> T put(T entity) {
		checkNotNull(entity);
		try {
			super._saveOrUpdateIsNew(entity);
			return entity;
		} catch (Exception e) {
			throw new PersistenceException(entity, "保存", "出错!", e);
		}
	}

	public <T> List<T> put(T entity, T... rest) {
		return this.put(Lists.asList(checkNotNull(entity), rest));
	}

	public <T> List<T> put(Iterable<? extends T> entities) {
		final List<T> result = Lists.newArrayList();
		for (T it : entities) {
			this.put(it);
			result.add(it);
		}
		return result;
	}

	public <T> T delete(T entity) {
		checkNotNull(entity);
		try {
			super._deleteEntity(entity);
			return entity;
		} catch (Exception e) {
			throw new PersistenceException(entity, "删除", "出错!", e);
		}
	}

	public <T> List<T> delete(T entity, T... rest) {
		return this.delete(Lists.asList(checkNotNull(entity), rest));
	}

	public <T> List<T> delete(Iterable<T> entities) {
		checkNotNull(entities);
		final List<T> result = Lists.newArrayList();
		for (T it : entities) {
			result.add(this.delete(it));
		}
		return result;
	}

	public <ID extends Serializable> void deleteById(Class<?> type, ID id) {
		checkNotNull(type);
		checkNotNull(id);
		try {
			super._deleteById(type, id);
		} catch (Exception e) {
			throw new PersistenceException(type, id, "删除", "出错!", e);
		}
	}

	public <ID extends Serializable> void deleteById(Class<?> type, ID id,
			ID... rest) {
		this.delete(checkNotNull(type), Lists.asList(checkNotNull(id), rest));
	}

	public <ID extends Serializable> void deleteById(Class<?> type,
			Iterable<ID> ids) {
		checkNotNull(ids);
		for (ID it : ids) {
			this.delete(type, it);
		}
	}

	@SuppressWarnings("unchecked")
	public <T, RT> List<RT> list(Query<T, RT> query) {
		return _search(checkNotNull(query.toSearch()));
	}

	public <T, RT> Page<RT> page(Query<T, RT> query) {
		checkNotNull(query);
		if (query.autoCount()) {
			@SuppressWarnings("unchecked")
			final SearchResult<RT> searchResult = super._searchAndCount(query
					.toSearch());
			final Page<RT> page = Page.get(query.thePage(), query.pagesize());
			return page.data(searchResult.getTotalCount(),
					searchResult.getResult());
		} else {
			final List<RT> results = this.list(query);
			final Page<RT> page = Page.get(query.thePage(), query.pagesize());
			return page.data(results);
		}
	}

	public <T, RT> int count(Query<T, RT> query) {
		return super._count(checkNotNull(query.toSearch()));
	}

	public <T> int count(Class<T> type) {
		return super._count(checkNotNull(type));
	}

	@SuppressWarnings("unchecked")
	public <T, RT> RT unique(Query<T, RT> query) {
		return (RT) super._searchUnique(checkNotNull(query.toSearch()));
	}

	public Session getSession() {
		return super.getSession();
	}

}
